package org.leiyang.netty.listener;

import io.netty.channel.ChannelHandlerContext;
import org.leiyang.common.dtos.LoginUser;
import org.leiyang.common.dtos.Message;
import org.leiyang.common.dtos.MessageType;
import org.leiyang.common.dtos.R;
import org.leiyang.common.holder.MsgServerAndClientHolder;
import org.leiyang.common.redis.ReactiveRedisCache;
import org.leiyang.common.util.Constants;
import org.leiyang.common.util.TokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;
import java.util.Objects;

/**
 * token校验，并保存客户端与SERVER的路由信息
 */
@Component
@Order(1)
public class ClientIdFilter implements INettyMsgFilter {
    private MsgServerAndClientHolder msgServerAndClientHolder;
    private ReactiveRedisCache redisCache;

    public MsgServerAndClientHolder getMsgServerAndClientHolder() {
        return msgServerAndClientHolder;
    }
    @Autowired
    public void setMsgServerAndClientHolder(MsgServerAndClientHolder msgServerAndClientHolder) {
        this.msgServerAndClientHolder = msgServerAndClientHolder;
    }

    public ReactiveRedisCache getRedisCache() {
        return redisCache;
    }
    @Autowired
    public void setRedisCache(ReactiveRedisCache redisCache) {
        this.redisCache = redisCache;
    }

    @Override
    public Mono<Boolean> chain(Message message, ChannelHandlerContext ctx, String serverIp, String port, Boolean httpFormat) {
        //说明该Message是进行token校验的
        if(message.getMessageType() == MessageType.TOKEN) {
            //进行token校验
            return checkToken(message.getToken()).flatMap(checkResult -> {
                logger.info("ClientIdFilter里校验Token是否通过: {}", checkResult);
                if(checkResult) {
                    //从token里解析出userId或者从message里获取
                    String clientId = buildClientId(message.getFromUserId(), ctx);
                    //server端会保存客户端与服务端的路由关系
                    return msgServerAndClientHolder.gotConnectedServerInfo(clientId, ctx, serverIp, port)
                            .map(result -> {
                                logger.info("gotConnectedServerInfo: {}", result);
                                sendResponse(ctx, R.ok(null, "token"), channelHandlerContext -> {}, httpFormat);
                                logger.info("finish sendResponse");
                                return false;
                            });
                }
                //token无效，则发送reps之后，主动断开客户端的连接
                sendResponse(ctx, R.fail("token"), channelHandlerContext -> channelHandlerContext.channel().close(), httpFormat);
                return Mono.just(false);
            });
        }
        return Mono.just(true);
    }

    /**
     * 构建ClientId，格式: {userId}@{clientIp}
     * @param userId 用户ID
     * @param ctx ctx
     * @return ClientId
     */
    private String buildClientId(Long userId, ChannelHandlerContext ctx) {
        InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        return userId + "@" + socketAddress.getAddress().getHostAddress().replaceAll("\\.", "");
    }

    private Mono<Boolean> checkToken(String token) {
        LoginUser loginUser = TokenUtil.parseToken(token);
        if(Objects.isNull(loginUser)) {
            return Mono.just(false);
        }
        return redisCache.keyExist(getTokenKey(token));
    }

    /**
     * 获取缓存key
     */
    private String getTokenKey(String token)
    {
        return Constants.ACCESS_TOKEN.concat(token);
    }
}
